﻿#region Using

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

using Common.Base;

using CommunicationLibrary.TransportData;

using GameLogic.External;

using ServerApplication.Enums;
using ServerApplication.Interfaces;
using ServerApplication.Networking;

#endregion

namespace ServerApplication.GameSession
{
    public class GameSession
    {
        private readonly IGame game;

        private readonly IGameSessionManager manager;

        private readonly PausingManager pausingManager;

        private readonly List<ServerPlayer> players;

        public GameSession(IGame game, IEnumerable<ServerPlayer> players, IGameSessionManager manager)
        {
            this.game = game;
            this.game.AfterUpdate += this.HandleGameState;
            this.game.OnPlayerLost += this.HandlePlayerLoose;
            this.game.OnGameOver += this.HandleGameOver;
            this.game.OnPaused += this.HandleGamePaused;
            this.game.OnResumed += this.HandleGameResumed;

            var serverPlayers = players as IList<ServerPlayer> ?? players.ToList();
            this.players = new List<ServerPlayer>(serverPlayers);

            this.manager = manager;

            foreach (var serverPlayer in serverPlayers)
            {
                if (serverPlayer.ServerPlayerState == ServerPlayerState.Disconnected)
                {
                    this.game.ManualPlayerLoose(serverPlayer);
                }

                serverPlayer.Disconnected += this.HandlePlayerDisconnect;
            }

            this.pausingManager = new PausingManager();
            this.pausingManager.AutoResume += this.ResumeGameManually;
        }

        private void HandleGameResumed(object sender, EventArgs e)
        {
            new Task(() => this.manager.HandleGameResumed(this.players.ToList())).Start();
        }

        private void HandleGamePaused(object sender, EventArgs e)
        {
            new Task(() => this.manager.HandleGamePaused(this.players.ToList())).Start();
        }

        private void ResumeGameManually(object sender, EventArgs e)
        {
            this.game.Resume();
        }

        private void HandlePlayerDisconnect(object sender, EventArgs e)
        {
            var player = sender as ServerPlayer;
            this.game.ManualPlayerLoose(player);
        }

        private void HandleGameOver(object sender, PlayerEventArgs e)
        {
            new Task(() => this.manager.HandleGameOver(this.game, e.Player.GenerateIdentifiable(), this)).Start();
        }

        private void HandlePlayerLoose(object sender, PlayerEventArgs e)
        {
            new Task(() => this.manager.PublishPlayerLoose(e.Player.GenerateIdentifiable(), this.players.ToList())).Start();
            this.players.RemoveAll(x => x.Equals(e.Player));
        }

        private void HandleGameState(object sender, EventArgs e)
        {
            var gamestate = sender as IGameState;

            new Task(() => this.manager.PublishGameState(new GameState(gamestate), this.players.ToList())).Start();
        }

        public bool Contains(Identifiable player)
        {
            return this.players.Contains(player);
        }

        public Result Execute(ICommand cmd)
        {
            return this.game.Execute(cmd);
        }

        public Result Pause(ServerPlayer player)
        {
            var result = this.pausingManager.Pause(player);

            if (result != Result.Success)
            {
                return result;
            }

            this.game.Pause();

            return Result.Success;
        }

        public void Quit(ServerPlayer player)
        {
            this.game.ManualPlayerLoose(player);
            player.ServerPlayerState = ServerPlayerState.Disconnected;
        }

        public Result Resume(ServerPlayer player)
        {
            var result = this.pausingManager.Resume(player);

            if (result != Result.Success)
            {
                return result;
            }

            this.game.Resume();

            return Result.Success;
        }
    }
}